/** ****************************************************************************
 * @file 	gpio.c
 * @author 	mojinpan
 * @copyright(c) 2018-2020 mojinpan. All rights reserved.
 * @brief 	基于tbox的gpio库
 *
 * @version 	V1.0
 * @date 	2025-05-04
 * @details
 * 1.
 *******************************************************************************/
#include "gpio.h"
#include <tbox/base/log.h>
#include <fstream>
#include <string>
#include <fcntl.h>
#include <unistd.h>
#include <tbox/event/loop.h>
#include <tbox/event/fd_event.h>
#include <sys/epoll.h>
#include <memory> 

using namespace std;
using namespace tbox;
using namespace tbox::event;

/** ****************************************************************************
* @brief  构造函数 
* @param  pin: GPIO引脚编号，dir_out: GPIO方向，val: GPIO初始值
* @return 无
* @note   当GPIO为输出时，val为true时，输出高电平，val为false时，输出低电平
*         当GPIO为输入时，读取GPIO的值
*         当GPIO为输入时，读取GPIO的值  
*******************************************************************************/
Gpio::Gpio(int pin, bool dir_out, bool val)
{
    dir_out_ = dir_out;
    pin_ = pin;
    std::string gpio_path = "/sys/class/gpio/";
    std::string export_path = gpio_path + "export";
    std::string gpio_pin_path = gpio_path + "gpio" + std::to_string(pin) + "/";
    std::string direction_path = gpio_pin_path + "direction";
    std::string value_path = gpio_pin_path + "value";

    // 导出 GPIO
    std::ofstream export_file(export_path);
    if (export_file.is_open())
    {
        export_file << pin;
        export_file.close();
    }
    else
    {
        LogErr("Failed to export GPIO %d", pin);
        return;
    }

    // 设置方向
    fd_ = open(direction_path.c_str(), O_WRONLY);
    if (fd_ == -1)
    {
        LogErr("Failed to open direction file for GPIO %d", pin);
        return;
    }

    if (dir_out)
    {
        if (::write(fd_, "out", 3) != 3)
        {
            LogErr("Failed to set GPIO %d as output", pin);
            close(fd_);
            return;
        }
    }
    else
    {
        if (::write(fd_, "in", 2) != 2)
        {
            LogErr("Failed to set GPIO %d as input", pin);
            close(fd_);
            return;
        }
    }
    close(fd_);

    // 打开 value 文件
    if (dir_out)
    {
        fd_ = open(value_path.c_str(), O_WRONLY);
        if (fd_ == -1)
        {
            LogErr("Failed to open value file for GPIO %d", pin);
            return;
        }
        // 设置初始值
        const char *value_str = val ? "1" : "0";
        if (::write(fd_, value_str, 1) != 1)
        {
            LogErr("Failed to set initial value of GPIO %d", pin);
            close(fd_);
            return;
        }
        this->val_ = val;
    }
    else
    {
        fd_ = open(value_path.c_str(), O_RDONLY);
        if (fd_ == -1)
        {
            LogErr("Failed to open value file for GPIO %d", pin);
            return;
        }
    }
}
/** ****************************************************************************
* @brief  析构函数
*******************************************************************************/
Gpio::~Gpio() 
{
    if (fd_ != -1) {
        close(fd_);
        fd_ = -1;
    }
}

/** ****************************************************************************
* @brief  写GPIO
* @param  val: 要写入的值
* @return 成功返回true，失败返回false
* @note   当GPIO为输出时，val为true时，输出高电平，val为false时，输出低电平
*         当GPIO为输入时，读取GPIO的值
*         当GPIO为输入时，读取GPIO的值
*******************************************************************************/
bool Gpio::write(bool val)
{
    if (fd_ == -1)
    {
        LogErr("GPIO file descriptor is invalid");
        return false;
    }

    const char *value_str = val ? "1" : "0";
    if (lseek(fd_, 0, SEEK_SET) == -1)
    {
        LogErr("Failed to seek in value file for GPIO");
        return false;
    }
    if (::write(fd_, value_str, 1) != 1)
    {
        LogErr("Failed to set value of GPIO");
        return false;
    }
    this->val_ = val;
    return true;
}
/** ****************************************************************************
* @brief  读GPIO
* @return 成功返回true，失败返回false
* @note   当GPIO为输出时，读取GPIO的值
*         当GPIO为输入时，读取GPIO的值
*******************************************************************************/
bool Gpio::read(void)
{
    if (dir_out_) return val_;
    if (!dir_out_)return false;

    if (lseek(fd_, 0, SEEK_SET) == -1)
    {
        LogErr("Failed to seek in value file for GPIO");
        return false;
    }
    char val;
    if (::read(fd_, &val, 1) != 1)
    {
        LogErr("Failed to read value of GPIO");
        return false;
    }
    return val == '1';
}

/** ****************************************************************************
* @brief  设置输入回调函数
* @param  cb: 回调函数
* @return 无
* @note   当GPIO为输入时，设置输入回调函数，当GPIO的值发生变化时，调用回调函数
*         回调函数的参数为GPIO的引脚号和GPIO的值
*         回调函数的参数为GPIO的引脚号和GPIO的值    
*******************************************************************************/
void Gpio::SetInputCallback(const cbFunc &cb)
{
    // 配置边缘触发检测
    std::string edge_path = "/sys/class/gpio/gpio" + std::to_string(pin_) + "/edge";
    std::ofstream edge_file(edge_path);
    if(edge_file.is_open()) {
        edge_file << "both";  // 配置为双边沿触发
        edge_file.close();
    } else {
        LogErr("Failed to configure edge detection for GPIO %d", pin_);
        return;
    }

    cb_ = cb;
    char buf[1];
    lseek(fd_, 0, SEEK_SET);
    ::read(fd_, buf, 1);
    auto sp_loop = std::unique_ptr<Loop>(Loop::New());
    if (sp_loop == nullptr) 
    {
        LogErr("fail, exit");
        return;
    }
    
    int epoll_fd = epoll_create1(0);
    if (epoll_fd == -1) {
        LogErr("Failed to create epoll instance");
        return;
    }

    struct epoll_event event;
    event.events = EPOLLPRI | EPOLLET;
    event.data.fd = fd_;

    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd_, &event) == -1) {
        LogErr("Failed to add GPIO to epoll");
        close(epoll_fd);
        return;
    }

    // 使用shared_ptr管理epoll_fd
    auto epoll_fd_ptr = std::make_shared<int>(epoll_fd);
    sp_loop->runInLoop([this, epoll_fd_ptr,&sp_loop]{
        struct epoll_event events[1];
        while (true) {
            int n = epoll_wait(*epoll_fd_ptr, events, 1, -1);
            if (n == -1) {
                LogErr("epoll_wait error");
                break;
            }
            if (events[0].events & EPOLLPRI) { 
                LogErr("key event!!");
                cb_(pin_, this->read());
            }
        }
        close(*epoll_fd_ptr);  // 确保关闭epoll文件描述符
        sp_loop->exitLoop();
    });

    sp_loop->runLoop(Loop::Mode::kForever);
}


/*********************************END OF FILE**********************************/
